/**
 * @author wItspirit
 * 28-mei-2005
 * RestFlickrApi.java
 */

package org.scohen.juploadr.uploadapi.zooomr;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.prefs.Preferences;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Decorations;
import org.scohen.juploadr.app.ImageAttributes;
import org.scohen.juploadr.app.JUploadr;
import org.scohen.juploadr.app.PhotoSet;
import org.scohen.juploadr.app.UploadImage;
import org.scohen.juploadr.event.UploadCompleteEvent;
import org.scohen.juploadr.event.UploadStatusMonitor;
import org.scohen.juploadr.ui.MessageUtils;
import org.scohen.juploadr.ui.PopupHandler;
import org.scohen.juploadr.ui.ProgressDialog;
import org.scohen.juploadr.uploadapi.Account;
import org.scohen.juploadr.uploadapi.CommunicationException;
import org.scohen.juploadr.uploadapi.ImageUploadApi;
import org.scohen.juploadr.uploadapi.ProtocolException;
import org.scohen.juploadr.uploadapi.UploadFailedException;
import org.scohen.juploadr.uploadapi.abilities.Abilities;
import org.scohen.juploadr.uploadapi.abilities.DefaultAbilities;
import org.scohen.juploadr.uploadapi.abilities.OrganizeAbilities;
import org.scohen.juploadr.uploadapi.zooomr.authentication.ZooomrAccount;
import org.scohen.juploadr.uploadapi.zooomr.authentication.ZooomrAuthEventDirector;
import org.scohen.juploadr.uploadapi.zooomr.photosets.GetPhotosetList;
import org.scohen.juploadr.uploadapi.zooomr.tags.GetListUser;
import org.scohen.juploadr.uploadapi.zooomr.ui.ZoomrAccountForm;
import org.scohen.juploadr.uploadapi.zooomr.ui.ZooomrMetadataForm;
import org.scohen.juploadr.uploadapi.zooomr.ui.ZooomrPopupHandler;
import org.scohen.juploadr.uploadapi.zooomr.upload.ZooomrUpload;
import org.scohen.juploadr.util.NonNullList;

/**
 * This is the REST based implementation of the FlickrApi
 */
public class ZooomrRestApi extends ImageUploadApi {
    public static final String ZOOOMR_SHARED_SECRET = "fb2377b77bd2639c"; //$NON-NLS-1$
    public static final String ZOOOMR_API_KEY = "6dd0b757dc9013189d2f250d0385320dc7f291d7"; //$NON-NLS-1$

    private Class[] configClasses = { ZoomrAccountForm.class, ZooomrMetadataForm.class };
    private static NonNullList<PhotoSet> photoSets = new NonNullList<PhotoSet>();
    private static Map<Class,List<PostUploadAction>> unfinishedActions = new HashMap<Class, List<PostUploadAction>>();
    private static ZooomrAccount currentAccount;
    private static final String DEFAULT_ACCOUNT_KEY = ZooomrRestApi.class.getName() + ".defaultAccount"; //$NON-NLS-1$
    private static Preferences prefs = Preferences.userNodeForPackage(ZooomrRestApi.class);

    private static ZooomrRestApi instance = new ZooomrRestApi();

    public static synchronized ImageUploadApi getInstance() {
        return instance;
    }

    /**
     * 
     */
    public ZooomrRestApi() {

    }

    public String getSiteName() {
        return "Zooomr"; //$NON-NLS-1$
    }

    /**
     * @see org.scohen.juploadr.uploadapi.ImageUploadApi#upload(org.scohen.juploadr.uploadapi.UserInfo,
     *      org.scohen.juploadr.app.UploadImage,
     *      org.scohen.juploadr.event.UploadStatusMonitor)
     */
    public void upload(UploadImage imageAttributes, UploadStatusMonitor monitor) throws UploadFailedException,
            CommunicationException {
        if (!isCurrentAccountAuthenticated()) {
            try {
                authenticate(getCurrentAccount());
            } catch (ProtocolException e) {
                throw new UploadFailedException(e.getMessage(), e);
            }
        }
        ZooomrUpload flickrUpload = new ZooomrUpload(imageAttributes, monitor);
        try {
            flickrUpload.execute();
        } catch (ProtocolException e) {
            throw new UploadFailedException(e.getMessage(), e);
        }
    }

    public Account getCurrentAccount() {
        if (currentAccount == null) {
            Map accounts = ZooomrAccount.allAccounts();
            if (currentAccount == null) {
                currentAccount = ZooomrAccount.getNullAccount();
            }

            if (accounts.size() > 0 && accounts.get(prefs.get(DEFAULT_ACCOUNT_KEY, "")) != null) { //$NON-NLS-1$
                currentAccount = (ZooomrAccount) accounts.get(prefs.get(DEFAULT_ACCOUNT_KEY, "")); //$NON-NLS-1$

            }
        }
        return currentAccount;

    }

    public Account newAccount(String name) {
        return new ZooomrAccount(name);
    }

    public void setCurrentAccount(Account account) {
        if (account != null && !(account instanceof ZooomrAccount)) {
            throw new IllegalArgumentException(Messages.getString("juploadr.zooomr.account.type.incorrect", account.getClass().getName(), ZooomrAccount.class.getName())); //$NON-NLS-1$
        }

        currentAccount = (ZooomrAccount) account;
        if (currentAccount.getUsername() != null) {
            prefs.put(DEFAULT_ACCOUNT_KEY, currentAccount.getUsername());
        }
    }

    public boolean isCurrentAccountNullAccount() {
        return getCurrentAccount().equals(ZooomrAccount.getNullAccount());
    }

    public List<? extends Account> allAccounts() {
        return new LinkedList<Account>(ZooomrAccount.allAccounts().values());
    }

    public boolean authenticate(Account account) throws ProtocolException, CommunicationException {
        ZooomrAuthEventDirector eventDirector = new ZooomrAuthEventDirector();
        return eventDirector.execute((ZooomrAccount) account);
    }

    public void initialize() {
        if (isCurrentAccountAuthenticated()) {
            Thread getPhotoSets = new Thread() {
                public void run() {
                    GetPhotosetList getList = new GetPhotosetList();
                    executeMethod(getList);

                }
            };

            Thread getTags = new Thread() {
                public void run() {
                    GetListUser tags = new GetListUser();
                    executeMethod(tags);
                    getCurrentAccount().setUserTags(tags.getTags());
                }
            };

            getPhotoSets.start();
            getTags.start();
        }
    }

    /**
     * Zooomr doesn't have the concept of photosets. This method should never be
     * called.
     */
    public PhotoSet newPhotoSet(ImageAttributes primaryPhoto) {
        throw new UnsupportedOperationException("Zooomr does not support photo sets");
    }

    @Override
    public PhotoSet newPhotoSet(String name, String description, ImageAttributes primaryPhoto) {
        return newPhotoSet(primaryPhoto);
    }

    public Class[] getConfiurationDialogs() {
        return configClasses;
    }

    public PopupHandler windowRightClicked(Decorations parent, JUploadr clicked) {
        return new ZooomrPopupHandler(parent, clicked);
    }

    public PopupHandler thumbnailRightClicked(Decorations parent, JUploadr uploadr, ImageAttributes clicked) {
        // if the image isn't selected, de-select any selected images
        // and select this one.
        if (!clicked.isSelected()) {
            Collection allAttributes = uploadr.getImageAttributes().values();
            Iterator iter = allAttributes.iterator();
            while (iter.hasNext()) {
                ImageAttributes atts = (ImageAttributes) iter.next();
                atts.setSelected(atts.equals(clicked));
            }
        }
        uploadr.refresh();
        return new ZooomrPopupHandler(parent, uploadr);
    }

    public static void setPhotoSets(List<PhotoSet> sets) {
        if (sets instanceof NonNullList) {
            photoSets = (NonNullList<PhotoSet>) sets;
        } else {
            photoSets = new NonNullList<PhotoSet>(sets);
        }
    }

    public static void addPhotoSet(PhotoSet set) {
        photoSets.add(set);
        Collections.sort(photoSets);
    }

    public void postProcessUpload(UploadCompleteEvent e, ImageAttributes atts) {
        atts.setPhotoId(e.getResponse().getPhotoId());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.scohen.juploadr.uploadapi.ImageUploadApi#postProcessAllUploads(java.util.Collection)
     */
    public void postProcessAllUploads(Collection<? extends UploadImage> images) {
        // Iterator iter = photoSets.iterator();
        CreateAllPhotosetsTask createTask = new CreateAllPhotosetsTask(photoSets, this);
        ProgressDialog pd;
        if (createTask.hasSetsToCreate()) {
            pd = new ProgressDialog(JUploadr.window, createTask);
            pd.execute();
        }

        AddToPhotosetTask addTask = new AddToPhotosetTask(photoSets, this);
        if (addTask.hasSetsToAdd()) {
            pd = new ProgressDialog(JUploadr.window, addTask);
            pd.execute();
        }
        rotateImages(images);
        // ok, since we've updated the photosets, re-get them to make sure we're
        // in sync
        GetPhotosetList getPhotoSets = new GetPhotosetList();
        executeMethod(getPhotoSets);

        while (unfinishedActions.size() > 0 && showUnfinishedUploadErrorMessage()) {
            retryUnfinishedActions();
        }

        photoSets.clear();
        GetPhotosetList getList = new GetPhotosetList();
        executeMethod(getList);
        photoSets = new NonNullList<PhotoSet>(getList.getPhotoSets());

    }

    private void retryUnfinishedActions() {
        List<PostUploadAction> actions = new LinkedList<PostUploadAction>();
        
        for(List<PostUploadAction> next: unfinishedActions.values()){
            
            actions.addAll(next);
        }
        unfinishedActions.clear();
        Iterator iter = actions.iterator();
        while (iter.hasNext()) {
            ZooomrMethod method = (ZooomrMethod) iter.next();
            executeMethod(method);
        }
    }

    /**
     * 
     */
    private boolean showUnfinishedUploadErrorMessage() {
        StringBuffer errorMessage = new StringBuffer();
        errorMessage.append(Messages.getString("juploadr.zooomr.error.after.upload")); //$NON-NLS-1$
        Iterator failureClasses = unfinishedActions.keySet().iterator();
        while (failureClasses.hasNext()) {
            List actions = getFailedActions((Class) failureClasses.next());
            if (actions != null) {
                Iterator actionIter = actions.iterator();
                while (actionIter.hasNext()) {
                    PostUploadAction method = (PostUploadAction) actionIter.next();
                    errorMessage.append(Messages.getString("juploadr.zooomr.failed.to")); //$NON-NLS-1$
                    errorMessage.append(method.getErrorText());
                    errorMessage.append("\n"); //$NON-NLS-1$
                }
            }

        }
        errorMessage.append(Messages.getString("juploadr.zooomr.option.retry")); //$NON-NLS-1$
        return MessageUtils.showMessage(errorMessage.toString(), Messages
                .getString("juploadr.zooomr.actions.failed"), SWT.ICON_WARNING | SWT.YES | SWT.NO); //$NON-NLS-1$

    }

    private void rotateImages(Collection<? extends UploadImage> images) {
        final Collection coll = getImagesToRotate(images);
        if (coll.size() > 0) {
            RotateTask rt = new RotateTask(coll, this);
            ProgressDialog pd = new ProgressDialog(JUploadr.window, rt);
            pd.execute();
        }
    }

    private void addFailedAction(PostUploadAction failed) {
        List<PostUploadAction> failedActions = unfinishedActions.get(failed.getClass());
        if (failedActions == null) {
            failedActions = new LinkedList<PostUploadAction>();
        }
        if (!failedActions.contains(failed)) {
            failedActions.add(failed);
        }
        unfinishedActions.put(failed.getClass(), failedActions);
    }

    private List getFailedActions(Class clazz) {
        return (List) unfinishedActions.get(clazz);
    }

    private Collection getImagesToRotate(Collection<? extends UploadImage> allImages) {
        List<UploadImage> images = new LinkedList<UploadImage>();
        Iterator iter = allImages.iterator();
        while (iter.hasNext()) {
            UploadImage image = (UploadImage) iter.next();
            if (image.getRotation() != 0) {
                images.add(image);
            }
        }
        return images;
    }

    public boolean executeMethod(ZooomrMethod method) {
        try {
            method.execute();
            return true;
        } catch (ProtocolException e) {
        } catch (CommunicationException e) {
        }
        if (method instanceof PostUploadAction) {
            addFailedAction((PostUploadAction) method);
        }
        return false;
    }

    public Abilities getAbilities() {
        return new DefaultAbilities() {
            public OrganizeAbilities getOrganizeAbilities() {
                return new OrganizeAbilities() {
                    public boolean hasPhotoSets() {
                        return false;
                    }
                };
            }
        };
    }

}